#pragma once

/* 正确的hsync应该是ccsync和任意锁的组合*/
#include <ddblk/apds/combine_int.h>
#include <ddblk/apds/combine/ccsync.h>
#include <dbg.h>

/* 使用ccsync的node */
typedef struct _ccsyncnode _hsyncnode_t;

typedef struct _hsyncnuma {
	struct{
		_hsyncnode_t * tail;
	} CACHE_ALIGNED;
} _hsyncnuma_t;

#define HSYNC_RESEARVEBYTES_FOR_LOCKS	(HDEF_CACHE_LINE_BYTES)
/* hsync还需要定义锁类型 */
typedef struct _hsync
{
	_hsyncnuma_t nnodes[APDS_NUMA_NR];
	/* 给锁预留的空间 w*/
	byte_t researve_for_locks[HSYNC_RESEARVEBYTES_FOR_LOCKS];
} _hsync_t;


/* 指定全局锁 */
/* 依赖一个锁 */

OU2INFUN(lock, hsync_glk)

/* 实现接口 */

SAI void __hsync_init(_hsync_t *s, _hsyncnode_t *sentry[], INFUN(hsync_glk, lkop))
{
	for (word_t idx = 0; idx < APDS_NUMA_NR; idx++){
		s->nnodes[idx].tail = sentry[idx];
		sentry[idx]->next = NULL;
		sentry[idx]->flag = COMBINE_FLG_OWNED;
	}
	lkop->init(&s->researve_for_locks[0], HSYNC_RESEARVEBYTES_FOR_LOCKS);
}
SAI void *__hsync_destruct(_hsync_t *s, _hsyncnode_t *psentry[]){
	PARAM_USED(s, psentry);
	for (word_t idx = 0; idx < APDS_NUMA_NR; idx++){
		psentry[idx] = s->nnodes[idx].tail;
	}
	return (void *)s;
}
SAI _hsyncnode_t *__hsync_fetch(_hsyncnuma_t *s, apdsfunc_t *pfunc, apdsarg_t *args, _hsyncnode_t *node){
	apds_movargs(&node->args, args);
	node->func = pfunc;

	/* 确保已经清空 */
	node->flag = COMBINE_FLG_WAIT;
	node->next = NULL;
	/* cmb(); */
#ifdef COMBINEDIRECT_ENQUEUE
	_hsyncnode_t *cur_node = swp_rel(&s->tail, node);
	atm_st(&cur_node->next, node);
	return cur_node;
#else
	PARAM_USED(s);
	return node;
#endif
}

SAI apdsret_t __hsync_wait(_hsyncnuma_t *s, _hsyncnode_t **pnode, lock_t *glock, INFUN(hsync_glk, lkop), INFUN(cmbn_wait, waitpolicy)){
	_hsyncnode_t *next_node, *cur_node, *tmp_node, *tmp_node_next;
	hword_t help_count = 0;
	waitcnt_t cnt = -1;
	lkword_t flag;
	lkword_t flagwait = COMBINE_FLG_WAIT;
	locktoken_t token;
	
#ifdef COMBINEDIRECT_ENQUEUE
	PARAM_USED(s);
	next_node = (*pnode)->next;
	cur_node = *pnode;
#else
	next_node = *pnode;
	cur_node = swp_rel(&s->tail, next_node);
	atm_st(&cur_node->next, next_node);
#endif
	flag = atm_ld(&cur_node->flag);
	while (flag == COMBINE_FLG_WAIT)
	{
		cnt = waitpolicy->waitf(&cur_node->flag, &flagwait, cnt);
		flag = atm_ld(&cur_node->flag);
	}

	if (flag == COMBINE_FLG_COMPLETED)
	{
		mp_rmb(); /* 同一cache行且存在读依赖 */
		*pnode = cur_node;
		return apds_args_ret(&cur_node->args);
	}

	/* 竞争获取全局锁 */
	lkop->lock(glock, &token);

	tmp_node = cur_node;
	tmp_node_next = next_node;
	do
	{
		/* APDS_PREFETCH(tmp_node_next); */
		apds_set_ret(&tmp_node->args, apds_sync_single(tmp_node_next->func, &tmp_node_next->args));
		atm_strel(&tmp_node->flag, COMBINE_FLG_COMPLETED);
		tmp_node = tmp_node_next;
		if (++help_count >= APDS_HELP_QUOTA) break;
		tmp_node_next = tmp_node_next->next;
	}while (tmp_node_next);


	/* 释放全局锁 */
	lkop->unlock_relax(glock, token);
	/* 通知下一个 */
	atm_strel(&tmp_node->flag, COMBINE_FLG_OWNED);

	/* 唤醒线程 */
	lkword_t flag_owned = COMBINE_FLG_OWNED;
	waitpolicy->wakef(&tmp_node->flag, &flag_owned);

	*pnode = cur_node;
	return apds_args_ret(&cur_node->args);
}

#define DEF_HSYNC(name, numa_nr, cid2numa, lockname)				\
typedef _hsyncnode_t _##name##node_t;						\
typedef _hsync_t _##name##_t;							\
PARAM_NONULL(1) SAI _##name##_t *_##name##_init(void *ptr, size_t size, INFUN(cmbn_node, nodemgr))\
{\
	if (size < sizeof(_##name##_t)) return NULL;\
	_##name##_t *s = (_##name##_t *)ptr;\
	_##name##node_t *node[numa_nr];\
	for (cid_t idx = 0; idx < numa_nr; idx++){\
		node[idx] = (_##name##node_t *)nodemgr->get_lknode(sizeof(_##name##node_t));\
	}\
	__hsync_init(s, node, OUFUN_OF(lockname));\
	return s;\
}\
PARAM_NONULL(1) SAI void *_##name##_destruct(_##name##_t *s, INFUN(cmbn_node, nodemgr)){\
	_##name##node_t *node[numa_nr];\
	void *rptr = __hsync_destruct(s, node);\
	for (cid_t idx = 0; idx < numa_nr; idx++){\
		nodemgr->ret_lknode(node[idx], sizeof(_##name##node_t));\
	}\
	return rptr;\
}\
PARAM_NONULL(1, 2, 3) SAI _##name##node_t *_##name##_fetch (_##name##_t *s, apdsfunc_t *pfunc, apdsarg_t *args, apdstinfo_t *tinfo, INFUN(cmbn_node, nodemgr)){\
	cid_t numa_id = cid2numa(tinfo->cid);\
	_##name##node_t *node = (_##name##node_t *)nodemgr->get_lknode(sizeof(_##name##node_t));\
	return __hsync_fetch(&s->nnodes[numa_id], pfunc, args, node);\
}\
PARAM_NONULL(1, 2, 3) SAI apdsret_t _##name##_wait(_##name##_t *s, _##name##node_t *node, apdstinfo_t *tinfo, INFUN(cmbn_wait, waitpolicy), INFUN(cmbn_node, nodemgr)){\
	cid_t numa_id = cid2numa(tinfo->cid);\
	apdsret_t ret = __hsync_wait(&s->nnodes[numa_id], &node, (lock_t *)(&s->researve_for_locks[0]), OUFUN_OF(lockname), waitpolicy);\
	nodemgr->ret_lknode(node, sizeof(_##name##node_t));\
	return ret;\
}



